﻿using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Linq;
using System.Windows.Controls;
using System.IO;
using System.Reflection;
using System.Windows.Controls.Primitives;

namespace System
{
    public static class TabTipAutomationHelper
    {
        #region 属性
        //private static readonly ILog _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        /// <summary>
        /// TabletTip注册表路径
        /// </summary>
        private const string TabTipRegistryKeyName = @"HKEY_CURRENT_USER\Software\Microsoft\TabletTip\1.7";

        /// <summary>
        /// TabletTip自动打开的注册表项
        /// </summary>
        private const string TabTipAutoInvokeKey = "EnableDesktopModeAutoInvoke";

        /// <summary>
        /// 虚拟键盘是否固定在屏幕底部的注册表项
        /// </summary>
        private const string TabTipDockedKey = "EdgeTargetDockedState";

        /// <summary>
        /// TabletTip的类名
        /// </summary>
        private const string TabTipWindowClassName = "IPTip_Main_Window";

        private const string TabTipExecPath = @"C:\Program Files\Common Files\microsoft shared\ink\TabTip.exe";

        private const uint WS_VISIBLE = 0x10000000;
        private const int GWL_STYLE = -16;
        private const int WM_SYSCOMMAND = 0x0112;
        private const uint SC_CLOSE = 0xF060;
        private const int WS_DISABLED = 0x08000000;
        private const int DWMWA_CLOAKED = 14;


        /// <summary>
        /// 需要推起界面的类型集合
        /// </summary>
        private static readonly List<Type> _bindedUIElements = new List<Type>();

        /// <summary>
        /// 上一次抬起的高度
        /// </summary>
        private static double _lastMoveHeight = 0;

        /// <summary>
        /// 监控虚拟键盘是否关闭的Timer
        /// </summary>
        private static Timer _tabTipCloseMonitor = new Timer(TimerCallback, null, Timeout.Infinite, Timeout.Infinite);

        /// <summary>
        /// 监控Timer是否运行的标志
        /// </summary>
        private static bool _isMonitoring = false;

        /// <summary>
        /// 监控Timer的锁
        /// </summary>
        private static readonly object _monitoringLock = new object();

        /// <summary>
        /// 屏幕宽度
        /// </summary>
        private static int _screenWidth;

        /// <summary>
        /// 屏幕高度
        /// </summary>
        private static int _screenHeight;

        /// <summary>
        /// 要抬起的控件
        /// </summary>
        /// <remarks>
        /// Window不是最大化时是Window
        /// Window最大化时是Window的第一个Child
        /// 控件在Popup中时是Popup
        /// </remarks>
        private static FrameworkElement _moveElement;

        /// <summary>
        /// 抬起后屏幕截图
        /// </summary>
        private static Bitmap _moveBitmap;

        private static bool isMainWindow;

        /// <summary>
        /// 是否触摸了屏幕
        /// </summary>
        private static bool _hasTouchDown = false;

        /// <summary>
        /// 是否真的失去焦点
        /// </summary>
        private static bool _isRealLostFocus = false;

        private static IInputHostManagerBroker _inputHostManagerBroker;

        #endregion

        private static List<Action> _callbackActions = new List<Action>();

        private static List<Action> _callCloseActions = new List<Action>();

        public static void RegisterTabtipMoveUpHandler(Action action)
        {
            _callbackActions.Add(action);
        }

        public static void RegisterTabtipCloseHandler(Action action)
        {
            _callCloseActions.Add(action);
        }

        #region 事件处理

        /// <summary>
        /// 注册的控件的触摸按下事件
        /// </summary>
        private static void OnTouchDown(object sender, RoutedEventArgs args)
        {
            _hasTouchDown = true;
        }

        /// <summary>
        /// 注册的控件的触摸抬起事件
        /// </summary>
        private static async void OnMouseUp(object sender, RoutedEventArgs args)
        {
            try
            {
                if (_hasTouchDown && sender is UIElement targetControl)
                {
                    _hasTouchDown = false;
                    _isRealLostFocus = false;

                    #region 只读的TextBox不打开键盘
                    if (sender is TextBoxBase tb && tb.IsReadOnly)
                        return;
                    #endregion

                    Open();

                    #region 切换桌面后开一次会被系统关闭（需要调查原因），所以要再打开一次键盘。
                    {
                        await Task.Delay(50);

                        Open();
                    }
                    #endregion

                    await Task.Delay(400);

                    #region 抬起UI
                    {
                        Rect targetControlRect;             //目标控件的尺寸和位置
                        double moveHeight;                  //要抬起的高度
                        int tabletTipHeight;                //虚拟键盘的高度

                        #region 获取屏幕分辨率
                        {
                            var screenSize = targetControl.GetScreenSize();
                            _screenWidth = (int)screenSize.Item1;
                            _screenHeight = (int)screenSize.Item2;
                        }
                        #endregion

                        #region 获取虚拟键盘的高度
                        tabletTipHeight = (int)(_screenHeight * 0.4287);
                        #endregion

                        //获取目标控件的尺寸和位置
                        targetControlRect = GetControlRect(targetControl);

                        //计算抬起高度
                        moveHeight = _screenHeight - (targetControlRect.Bottom - _lastMoveHeight) - tabletTipHeight - 10;

                        if (moveHeight >= 0)
                        {
                            #region 不需要抬起
                            lock (_monitoringLock)
                            {
                                _isMonitoring = false;
                            }
                            _tabTipCloseMonitor.Change(Timeout.Infinite, Timeout.Infinite);
                            MoveBack();
                            #endregion
                        }
                        else
                        {
                            #region 需要向上抬起

                            //判断控件是否在Popup中
                            FrameworkElement popup = IsParentPopup(targetControl);
                            //isMainWindow = false;
                            if (popup != null)
                            {
                                #region 要抬起的是Popup
                                _moveElement = popup;

                                if (popup.RenderTransform is TranslateTransform == false)
                                {
                                    popup.RenderTransform = new TranslateTransform { Y = moveHeight };
                                }
                                else
                                {
                                    ((TranslateTransform)popup.RenderTransform).Y = moveHeight;
                                }
                                #endregion
                            }
                            else
                            {
                                #region 要抬起的是Window或它的子元素
                                //获取目标控件所在的窗口
                                Window moveWindow = Window.GetWindow(targetControl);
                                if (moveWindow.WindowState != WindowState.Maximized)
                                {
                                    #region 要抬起的是Window
                                    _moveElement = moveWindow;
                                    moveWindow.Top += moveHeight - _lastMoveHeight;
                                    #endregion
                                }
                                else
                                {
                                    #region 要抬起的是Window的第一个子元素
                                    //isMainWindow = true;
                                    _moveElement = (FrameworkElement)VisualTreeHelper.GetChild(moveWindow, 0);

                                    if (_moveElement.RenderTransform is TranslateTransform == false)
                                    {
                                        _moveElement.RenderTransform = new TranslateTransform { Y = moveHeight };
                                    }
                                    else
                                    {
                                        ((TranslateTransform)_moveElement.RenderTransform).Y = moveHeight;
                                    }
                                    #endregion
                                }
                                #endregion

                                _callbackActions?.ForEach(act =>
                                {
                                    try
                                    {
                                        act?.Invoke();
                                    }
                                    catch (Exception)
                                    {
                                        //ignore
                                    }
                                });
                            }

                            _lastMoveHeight = moveHeight;

                            //if (!isMainWindow)
                            //{
                            //    _moveBitmap = (Bitmap)Application.Current.MainWindow.GetScreenShot(_screenWidth / 2 - 50, _screenHeight - 40, 100, 30);
                            //}

                            #region 开启键盘监控Timer
                            //开启虚拟键盘关闭的监控
                            lock (_monitoringLock)
                            {
                                _isMonitoring = true;
                            }
                            _tabTipCloseMonitor.Change(800, Timeout.Infinite);
                            #endregion
                            #endregion

                        }
                    }
                    #endregion
                }
            }
            catch (Exception)
            {
                //ignore
            }
        }

        private static Rect GetControlRect(UIElement targetControl)
        {
            var tempRect = targetControl.RenderTransform.TransformBounds(new Rect(targetControl.PointToScreen(new System.Windows.Point(0, 0)), targetControl.RenderSize));
            var tempZoom = targetControl.GetScreenZoom();
            var rect = new Rect(tempRect.Left / tempZoom, tempRect.Top / tempZoom, tempRect.Width / tempZoom, tempRect.Height / tempZoom);
            return rect;
        }

        /// <summary>
        /// 注册的控件的失去焦点事件
        /// </summary>
        private static async void OnLostFocus(object sender, RoutedEventArgs args)
        {
            _isRealLostFocus = true;
            await Task.Delay(100);

            if (_isRealLostFocus)
            {
                try
                {
                    UIElement targetControl;  //目标控件（要进行输入的控件）

                    Close();

                    _isRealLostFocus = false;

                    lock (_monitoringLock)
                    {
                        _isMonitoring = false;
                    }
                    _tabTipCloseMonitor.Change(Timeout.Infinite, Timeout.Infinite);

                    if (_lastMoveHeight >= 0)
                    {
                        return;
                    }

                    targetControl = (UIElement)sender;

                    MoveBack();
                    _lastMoveHeight = 0;
                }
                catch (Exception)
                {
                    return;
                }
            }
        }

        /// <summary>
        /// 虚拟键盘关闭监控Timer到时
        /// </summary>
        private static void TimerCallback(object state)
        {
            lock (_monitoringLock)
            {
                if (_isMonitoring == false)
                {
                    return;
                }
            }

            if (_lastMoveHeight >= 0)
            {
                return;
            }

            if (IsTabTipClosed())
            {
                #region 虚拟键盘关闭了
                Application.Current.Dispatcher.Invoke(() =>
                {
                    MoveBack();
                });
                #endregion
            }
            else
            {
                #region 虚拟键盘没有关闭
                _tabTipCloseMonitor.Change(300, Timeout.Infinite);
                #endregion
            }
        }

        #endregion

        #region 方法

        /// <summary>
        /// 初始化
        /// </summary>
        /// <exception cref="Exception">注册表操作相关的异常</exception>
        public static void Init()
        {
            try
            {
                #region 将注册表中的虚拟键盘自动打开的开关设置为开
                {
                    int enableDesktopModeAutoInvoke;

                    enableDesktopModeAutoInvoke = (int)(Registry.GetValue(TabTipRegistryKeyName, TabTipAutoInvokeKey, -1) ?? -1);
                    if (enableDesktopModeAutoInvoke != 1)
                    {
                        Registry.SetValue(TabTipRegistryKeyName, TabTipAutoInvokeKey, 1);
                    }
                }
                #endregion

                #region 将注册表中虚拟键盘是否固定在屏幕下方的开关设置为固定
                {
                    int docked;

                    docked = (int)(Registry.GetValue(TabTipRegistryKeyName, TabTipDockedKey, 1) ?? 1);
                    if (docked == 1)
                    {
                        Registry.SetValue(TabTipRegistryKeyName, TabTipDockedKey, 0);
                    }
                }
                #endregion

                var brokerClass = new ImmersiveShellBroker();
                var broker = (IImmersiveShellBroker)brokerClass;
                _inputHostManagerBroker = broker.GetInputHostManagerBroker();
                Marshal.ReleaseComObject(broker);
            }
            catch (Exception)
            {
                //ignore
            }
        }

        /// <summary>
        /// 指定某个类型的控件在TouchUp时将界面推起，在LostFocus时将界面放下
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        public static void Register<T>() where T : UIElement
        {
            if (_bindedUIElements.Contains(typeof(T)))
            {
                return;
            }

            _bindedUIElements.Add(typeof(T));

            try
            {
                EventManager.RegisterClassHandler(typeof(T), UIElement.PreviewTouchDownEvent, new RoutedEventHandler(OnTouchDown), true);
                EventManager.RegisterClassHandler(typeof(T), UIElement.PreviewMouseUpEvent, new RoutedEventHandler(OnMouseUp), true);

                //注册LostFocus事件处理
                EventManager.RegisterClassHandler(typeof(T), UIElement.LostFocusEvent, new RoutedEventHandler(OnLostFocus), true);
            }
            catch (Exception)
            {
                //ignore
            }
        }

        /// <summary>
        /// 判断虚拟键盘是否关闭了
        /// </summary>
        /// <returns></returns>
        private static bool IsTabTipClosed()
        {
            if (_inputHostManagerBroker != null)
            {
                _inputHostManagerBroker.GetIhmLocation(out RECT rect, out DisplayMode mode);
                //_logger.Debug($"{rect.Left} {rect.Top} {rect.Right} {rect.Bottom} {mode}");
                return mode == DisplayMode.NotSupported || (rect.Left == 0 && rect.Top == 0 && rect.Right == 0 && rect.Bottom == 0);
            }

            return false;
        }

        /// <summary>
        /// 将目标窗口复原
        /// </summary>
        private static void MoveBack()
        {
            if (_lastMoveHeight >= 0)
            {
                return;
            }

            if (_moveElement == null)
            {
                return;
            }

            try
            {
                if (_moveElement is Window moveWindow)
                {
                    moveWindow.Top -= _lastMoveHeight;
                }
                else
                {
                    _moveElement.RenderTransform = new TranslateTransform { Y = 0 };
                }
            }
            catch (Exception)
            {
                //ignore
            }

            _lastMoveHeight = 0;
            _moveElement = null;
        }

        /// <summary>
        /// 判断控件是否在Popup之中
        /// </summary>
        /// <param name="element">控件</param>
        /// <returns>控件所在的Popup（如果控件不在Popup中，则返回null）</returns>
        private static FrameworkElement IsParentPopup(DependencyObject element)
        {
            DependencyObject loopEle;              //循环变量
            FrameworkElement targetPopup = null;   //目标Popup

            loopEle = element;

            while (loopEle is Window == false
                && loopEle != null)
            {
                loopEle = VisualTreeHelper.GetParent(loopEle);
                if (loopEle.GetType().ToString().Contains("PopupRoot"))
                {
                    targetPopup = (FrameworkElement)loopEle;
                    break;
                }
            }

            return targetPopup;
        }

        /// <summary>
        /// Close TabTip
        /// </summary>
        public static void Close()
        {
            try
            {
                SendMessage(GetTabTipWindowHandle().ToInt32(), WM_SYSCOMMAND, SC_CLOSE, 0);

                _callCloseActions?.ForEach(act =>
                {
                    try
                    {
                        act?.Invoke();
                    }
                    catch (Exception)
                    {
                        //ignore
                    }
                });

            }
            catch (Exception)
            {
                //ignore
            }
        }

        /// <summary>
        /// Open TabTip
        /// </summary>
        public static void Open()
        {
            EnableTabTipOpenInDesctopModeOnWin10();

            Process.Start(TabTipExecPath);
        }

        public static void Open(UIElement element)
        {
            OnTouchDown(element, null);
            OnMouseUp(element, null);
        }

        private static void EnableTabTipOpenInDesctopModeOnWin10()
        {
            const string TabTipAutoInvokeKey = "EnableDesktopModeAutoInvoke";

            int EnableDesktopModeAutoInvoke = (int)(Registry.GetValue(TabTipRegistryKeyName, TabTipAutoInvokeKey, -1) ?? -1);
            if (EnableDesktopModeAutoInvoke != 1)
                Registry.SetValue(TabTipRegistryKeyName, TabTipAutoInvokeKey, 1);
            Registry.SetValue(TabTipRegistryKeyName, "LastUsedModalityWasHandwriting", 0);
            Registry.SetValue(TabTipRegistryKeyName, "KeyboardLayoutPreference", 1);
        }

        [DllImport("user32.dll")]
        private static extern int SendMessage(int hWnd, uint msg, uint wParam, int lParam);

        [DllImport("user32.dll")]
        private static extern IntPtr FindWindow(string sClassName, string sAppName);

        [DllImport("user32.dll")]
        private static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);

        [DllImport("user32.dll", SetLastError = true)]
        static extern uint GetWindowLong(IntPtr hWnd, int nIndex);

        private static IntPtr GetTabTipWindowHandle() => FindWindow(TabTipWindowClassName, null);


        [DllImport("dwmapi.dll", EntryPoint = "DwmGetWindowAttribute")]
        private static extern int DwmGetWindowAttribute(IntPtr intPtr, int dwAttribute, out int pvAttribute,
            uint cbAttribute);


        #endregion

    }

    [ComImport, Guid("228826af-02e1-4226-a9e0-99a855e455a6")]
    class ImmersiveShellBroker
    {
    }

    [ComImport, Guid("9767060c-9476-42e2-8f7b-2f10fd13765c")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IImmersiveShellBroker
    {
        void Dummy();
        IInputHostManagerBroker GetInputHostManagerBroker();
    }

    [ComImport, Guid("2166ee67-71df-4476-8394-0ced2ed05274")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IInputHostManagerBroker
    {
        void GetIhmLocation(out RECT rect, out DisplayMode mode);
    }

    [StructLayout(LayoutKind.Sequential)]
    struct RECT
    {
        public int Left, Top, Right, Bottom;
    }

    enum DisplayMode
    {
        NotSupported = 0,
        Floating = 2,
        Docked = 3,
    }

    public enum TabtipTypes
    {
        Unknown = 0,

        Keyboard = 1,

        Ink = 2
    }
}
